package coreModel;

import hitExceptions.InvalidOperationException;

import java.util.Collection;
import java.util.ArrayList;
import java.util.Stack;

import java.util.Iterator;

import common.Result;

import dataPersistence.TransactionManager;
import dataPersistence.dao.IStorageUnitDAO;
import dataPersistence.dto.StorageUnitDTO;

import reports.directors.IVisitor;



public class StorageUnitManager extends SerializableImplementer implements INode{

	private Collection<StorageUnit> storageUnits;


	/**
	 * Constructer 	
	 */
	public StorageUnitManager()
	{
		storageUnits = new ArrayList<StorageUnit>();
	}



	/**
	 * Return the storageUnit object that corresponds to the unitName
	 * @param unitName name of storage unit
	 * @return the storage unit that corresponds to that name
	 */
	public StorageUnit getStorageUnit(String unitName)
	{

		Iterator<StorageUnit> it = storageUnits.iterator();

		while(it.hasNext())
		{
			StorageUnit temp = it.next();
			if(temp.getName().equals(unitName)) return temp;
		}

		return null;
	}


	/**
	 * This method checks if passed in storage unit is already present
	 * @param storageunit storageunit
	 * @return true if storageunit found else false
	 */
	public boolean containsStorageUnit(StorageUnit storageunit)
	{

		return storageUnits.contains(storageunit);
	}



	/**
	 * Checks to see if a storage unit can be removed.
	 * @param unit the storageUnit that wants to be removed
	 * @return true iff the unit is empty of all items
	 */
	public boolean canRemoveStorageUnit(StorageUnit unit)
	{
		if(unit.canRemoveContainer()) return true; else return false;
	}



	/**
	 * This method adds a storage unit 	
	 * @throws InvalidOperationException 
	 */
	public void addStorageUnit(StorageUnit storageunit, boolean updateDatabase) throws InvalidOperationException
	{

		if (getStorageUnit(storageunit.getName()) != null)
			throw new InvalidOperationException("Can't add two storage units with same name!");

		storageUnits.add(storageunit);

		if(updateDatabase)
		{
			//data persistence
			Result result = new Result();
			if(!result.getStatus()) System.err.println(result.getMessage());
			if (!HIT.getInstance().isLoadingFromDataBase())
			{
				IStorageUnitDAO dao = HIT.getInstance().getFactory().getStorageUnitDAO();
				StorageUnitDTO dto = new StorageUnitDTO(storageunit);
				result = dao.add(dto);
				if(!result.getStatus()) System.err.println(result.getMessage());
			}
			
		}
	}


	/**
	 * This method removes the given storage unit 	
	 * @param storageunit storage unit to remove
	 * @return void
	 */
	public void removeStorageUnit(StorageUnit storageunit)
	{
		storageUnits.remove(storageunit);
	}



	/**
	 * This method returns all storage units that are currently created	
	 * @return a collections of storage units
	 */
	public Collection<StorageUnit> getStorageUnits()
	{
		Collection<StorageUnit> tmp = new ArrayList<StorageUnit>();
		tmp.addAll(storageUnits);
		return tmp;
	}


	/**
	 * Updates the name and children of a storageUnit by updating
	 *  the object stored in the storageUnit collection
	 * @param originalUnit original unit
	 * @param modifiedStorageUnit unit with up to date info
	 */
	public void editStorageUnit(StorageUnit originalUnit, StorageUnit modifiedStorageUnit)
	{
		//assuming that orgnialUnit is a reference to the obj in the storageUnits array


		try
		{
			originalUnit.setName(modifiedStorageUnit.getName()); //change name
		} 
		catch (InvalidOperationException e)
		{
			//in thoery it will never get to this line of code
			e.printStackTrace();
		}


		Collection<ProductGroup> children= modifiedStorageUnit.getChildren();

		//remove all old children



	}

	public static ProductContainer getParentStorageUnit(ProductContainer parent)
	{
		if (parent instanceof StorageUnit)
			return (StorageUnit) parent;
		else if(((ProductGroup)parent).getParent() instanceof StorageUnit)
			return ((ProductGroup)parent).getParent();
		else
			return getParentStorageUnit(((ProductGroup)parent).getParent());
	}



	@Override
	public void postOrderAccept(IVisitor visitor)
	{

		for(StorageUnit su: storageUnits)
		{
			su.postOrderAccept(visitor);
		}
		visitor.visit(this);

	}



	@Override
	public void preOrderAccept(IVisitor visitor)
	{
		visitor.visit(this);
		for(StorageUnit su: storageUnits)
		{
			su.preOrderAccept(visitor);
		}

	}

	public ProductContainer getContainerByID(String id)
	{
		Stack<ProductContainer> myLittleStack = new Stack<ProductContainer>();

		for(StorageUnit su : storageUnits)
		{
			myLittleStack.add(su);
		}

		while(!myLittleStack.isEmpty())
		{
			ProductContainer tmp = myLittleStack.pop();
			if(tmp.getContainerID().toString().equals(id))
				return tmp;
			else
			{
				for( ProductContainer pc : tmp.getChildren())
				{
					myLittleStack.push(pc);
				}
			}
		}

		return null;

	}


}
